home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 April / EnigmA AMIGA RUN 17 (1997)(G.R. Edizioni)(IT)[!][issue 1997-04][EAR-CD].iso / EARCD / util / cli / examine.lha / Examine1.4 / Source / Examine.e next >
Text File  |  1996-11-20  |  21KB  |  625 lines

  1. /*
  2. **   Examine V1.4 r18 15/4/96
  3. **   Hand crafted from coffee and chocolate biscuits by Neil Carter
  4. **   Public domain 18/11/95
  5. **
  6. **   [See how long it takes me to get around to releasing things?!
  7. **   This is the (almost) unmodified source for V1.4.  If the tone of
  8. **   the comments seems 'conversational', it's not because I'm talking
  9. **   to you: it's because I'm talking to myself!  8-D
  10. **   (Constructive) criticism is welcomed - I'd like some feedback on
  11. **   my programming style.  I apologise if the long comments don't fit
  12. **   on your screen - I'm using a 7x9 font! -Neil]
  13. **
  14. **   Don't forget to change version numbers throughout!
  15. **
  16. **   The only problem with %h and %o is the way an extra space tends to turn up on the
  17. **   end of the string when the length is a multiple of two bytes.
  18. **
  19. **   Do I really want to implement %v?  *Sigh!*
  20. **
  21. **   Probably ought to provide directory scanning (LIST/S), subdirectory recursion (ALL/S)
  22. **   and pattern matching on the filename (implicit LIST/S).  However, if I do, I'll need to
  23. **   rearrange the variable handling a bit.  Specifically, some of the global variables will
  24. **   need to be made local again (fib, etc.) so I can use recursion.  Also, it'll be
  25. **   necessary to pass those local variables to the parsetoken() functions and so on.
  26. */
  27.  
  28. ->•Notes
  29. /*
  30.  
  31. Project description for Examine V1.4 (V2.0?)
  32.  
  33. Default behaviour, given a filename:
  34.  
  35.                Output "Filename:        %n\n
  36.                        Path:            %f\n
  37.                        Size in bytes:   %d\n
  38.                        Dir entry type:  %e\n
  39.                        FileID name:     %s\n
  40.                               code:     %i\n
  41.                               class:    %r\n"
  42.  
  43.  
  44. Default behaviour, given no filename:
  45.  
  46.                Output "Examine ?.? r? (?/?/?) written by Neil Carter.
  47.                        PUBLIC DOMAIN ONLY.
  48.                        (Template)"
  49.  
  50. Template:
  51.  
  52. Examine FILENAME,BRIEF/S,FULL/S,Q=QUICK/S,NOFILEID/S,FORMAT/K,NUMBYTES/K/N,
  53.         VERSION/S,ID/S EXE=EXECUTABLE/S,DIR=DIRECTORY/S,WINDOW/S
  54.  
  55. FILENAME:      Obvious
  56. BRIEF:         Output "Object %n is %z %s\n"
  57. FULL:          Output "Filename:        %n\n
  58.                        Path:            %f\n
  59.                        Comment:         %c\n
  60.                        Datestamp:       %t %d\n
  61.                        Size in bytes:   %d\n
  62.                        Size in blocks:  %b\n
  63.                        Protection:      %a\n
  64.                        Dir entry type:  %e\n
  65.                        Begins with:     $%h %o\n
  66.                        FileID name:     %s\n
  67.                               code:     %i\n
  68.                               class:    %r\n"
  69. QUICK:         Output "%s"
  70. NOFILEID:      Don't use FileID.library (just do an ASCII file check)
  71. FORMAT:        Output using the supplied output string
  72. NUMBYTES:      Number of bytes for %h and %o to display
  73. VERSION:       Output "%v"
  74. ID:            Output "%i"
  75. RETURNID:      Return RC=ID number
  76. EXECUTABLE:    Return RC=5 if FILENAME is an executable
  77. DIRECTORY:     Return RC=5 if FILENAME is a directory
  78. WINDOW:        Display output in a window instead of through STDOUT
  79.  
  80. BRIEF, FULL, QUICK, FORMAT, VERSION and ID options are mutually exclusive;
  81. the last one found will be used in case of conflict. NUMBYTES has no effect
  82. if FORMAT is not used. EXECUTABLE, DIRECTORY, or RETURNID can be used with
  83. any other options but are mutually exclusive with one another.  Note that
  84. the default is output if only EXECUTABLE, DIRECTORY or RETURNID is used.
  85.  
  86. Only the default, BRIEF, FULL, QUICK, FORMAT, ID and RETURNID modes open
  87. FileID.library (unless NOFILEID is used).
  88.  
  89. Options for FORMAT (pretty much the same as List LFORMAT, for simplicity):
  90.  
  91. %a   Protection bits (hsparwed)
  92. %b   Size in blocks
  93. %c   Comment
  94. %d   Date
  95. %e   Entry type
  96. %f   Full path
  97. %g   FileID global file class bits (sfgmiepx)
  98. %h   First few bytes hex dump (00000000 00000000)
  99. %i   FileID ID code
  100. %k   Disk key
  101. %l   Size in bytes
  102. %m
  103. %n   Filename
  104. %o   First few bytes text dump (.... ....)
  105. %p   Path as supplied
  106. %q
  107. %r   FileID global file class text
  108. %s   FileID type name
  109. %t   Time
  110. %u
  111. %v   Version ($VER:)
  112. %w   Weekday
  113. %x
  114. %y
  115. %z   "A" or "an" depending on the FileID type name
  116. %%   Just print a "%" sign
  117.  
  118. Other possible ideas:
  119.  
  120. ALL/S option to cause recursion into subdirectories?
  121. Pattern matching?
  122.  
  123. */
  124.  
  125. ->–
  126.  
  127. ->>> Compiler
  128. ->•Modules
  129. MODULE 'dos/dos','dos/datetime', 'fileid', 'libraries/fileid'
  130. ->–
  131. ->•Constants
  132. ->>> Exceptions
  133. ENUM EX_OK, EX_NO_FILEID, EX_DOS_ERROR, EX_ABOUT, EX_KICK, EX_DFORMAT_WITHOUT_FORMAT
  134.  
  135. ->>> Arguments (don't forget to change NUMARGS below!)
  136. ENUM AFILENAME, ABRIEF, AFULL, AQUICK, ANOFILEID, AFORMAT, ADFORMAT, ANUMBYTES,
  137.      ALIST, AALL, AVERSION, AID, ARETURNID, AEXE, ADIR, AWINDOW
  138.  
  139. ->>> ASCII tester
  140. ENUM PURE_ASCII, AMIGA_ASCII, OTHER_ASCII, BINARY, EMPTY_FILE
  141.  
  142. ->>> System
  143. CONST KICKVER=37              -> Any 2.04+ Kickstart
  144. CONST FILEIDVERS=2
  145.  
  146. ->>> Buffer sizes
  147. CONST BUFFER_SIZE=1200, FILENAME_LEN=256, DISKPROGNAME_LEN=110, DESCRIPTION_LEN=36,
  148.       OUTPUT_LEN=4096, WHOCARES=1000, DATE_LEN=50,
  149.       NUMARGS=16
  150. CONST ARGBUFFER_SIZE=NUMARGS*4
  151. ->–
  152.  
  153. ->>> Globals
  154. ->•Globals
  155. DEF fib=NIL:PTR TO fileinfoblock
  156. DEF fidb=NIL:PTR TO fileinfo
  157. DEF buffer=NIL:PTR TO CHAR, length=NIL
  158. DEF lock=NIL, handle=NIL
  159. DEF path:PTR TO CHAR
  160. DEF day=NIL:PTR TO CHAR, date=NIL:PTR TO CHAR, time=NIL:PTR TO CHAR
  161. DEF arguments:PTR TO LONG, rdargs=NIL
  162. DEF isdirectory=FALSE, numbytes=8
  163. ->–
  164.  
  165. ->>> Procedures
  166. ->•Main()
  167. PROC main() HANDLE
  168. ->>> Build an ENORMOUS stack frame!
  169.      DEF fullfilename[FILENAME_LEN]:STRING
  170.      DEF diskprogname[DISKPROGNAME_LEN]:STRING
  171.      DEF output[OUTPUT_LEN]:STRING           -> Should this be on the stack?
  172.      DEF longptr:PTR TO LONG, id=NIL:LONG, rc=NIL:LONG
  173.  
  174. ->>> Initialise
  175.      IF KickVersion(KICKVER)=FALSE THEN Raise(EX_KICK)
  176.      IF GetProgramName(diskprogname, 110)=0 THEN diskprogname:='Examine'
  177.      diskprogname:=FilePart(diskprogname)
  178.  
  179.      arguments:=NewR(ARGBUFFER_SIZE)
  180.      buffer:=NewR(BUFFER_SIZE)
  181.      path:=String(FILENAME_LEN)
  182.  
  183. ->>> Read arguments
  184.  
  185.      IF rdargs:=ReadArgs({template}, arguments, NIL)
  186.           StrCopy(fullfilename, arguments[AFILENAME])
  187.      ELSE
  188.           Raise(EX_DOS_ERROR)
  189.      ENDIF
  190.  
  191.      IF arguments[AFILENAME]=NIL THEN Raise(EX_ABOUT)       -> No arguments calls version string
  192.      IF arguments[ANUMBYTES] THEN numbytes:=Bounds(Long(arguments[ANUMBYTES]), 4, 32)
  193.  
  194. ->>> Load and set up file
  195.  
  196.      IF (lock:=Lock(fullfilename, ACCESS_READ))=0 THEN Raise(EX_DOS_ERROR)      -> Lock file
  197.      IF Examine(lock, NEW fib)                              -> Get FileInfoBlock
  198.           IF fib.direntrytype<0                             -> Is it a file?
  199.                IF (handle:=OpenFromLock(lock))              -> Open file
  200.                     length:=Read(handle, buffer, 1200)      -> Get first 1200 bytes
  201.                ELSE
  202.                     Raise(EX_DOS_ERROR)
  203.                ENDIF
  204.           ELSE
  205.                isdirectory:=TRUE                            -> Mark it as a directory
  206.           ENDIF
  207.      ELSE
  208.           Raise(EX_DOS_ERROR)
  209.      ENDIF
  210.      StrCopy(path, fullfilename, FilePart(fullfilename)-fullfilename) -> Get path + trailing : or /
  211.  
  212. ->>> Main logic
  213.  
  214.      IF arguments[AID]
  215.           formatoutput('%i', output)
  216.      ELSEIF arguments[AVERSION]
  217.           formatoutput('%v', output)
  218.      ELSEIF arguments[AFORMAT]
  219.           formatoutput(arguments[
  220.                        IF (isdirectory) AND (arguments[ADFORMAT]) THEN ADFORMAT ELSE AFORMAT],
  221.                        output)
  222.      ELSEIF arguments[ADFORMAT]
  223.           Raise(EX_DFORMAT_WITHOUT_FORMAT)
  224.      ELSEIF arguments[AQUICK]
  225.           formatoutput('%s', output)
  226.      ELSEIF arguments[AFULL]
  227.           formatoutput('Filename:        %n\n'+
  228.                        'Path:            %f\n'+
  229.                        'Comment:         %c\n'+
  230.                        'Datestamp:       %t %w, %d\n'+
  231.                        'Size in bytes:   %l\n'+
  232.                        'Size in blocks:  %b\n'+
  233.                        'Protection:      %a\n'+
  234.                        'Dir entry type:  %e\n'+
  235.                        'Begins with:     $%h "%o"\n'+
  236.                        'FileID name:     %s\n'+
  237.                        '       code:     %i\n'+
  238.                        '       class:    %r', output)
  239.      ELSEIF arguments[ABRIEF]
  240.           formatoutput('Object %n is %z %s', output)
  241.      ELSE -> Default
  242.           formatoutput('Filename:        %n\n'+
  243.                        'Path:            %f\n'+
  244.                        'Size in bytes:   %l\n'+
  245.                        'Dir entry type:  %e\n'+
  246.                        'FileID name:     %s\n'+
  247.                        '       code:     %i\n'+
  248.                        '       class:    %r', output)
  249.      ENDIF
  250.  
  251. ->>> Output the string
  252.      IF arguments[AWINDOW]
  253.           EasyRequestArgs(NIL, NEW [20, 0, diskprogname, '\s', 'Ok'], NIL, NEW [output])
  254.      ELSE
  255.           PrintF('\s\n', output)
  256.      ENDIF
  257.  
  258. ->>> Exception handler
  259. EXCEPT DO
  260.      IF exception=EX_KICK
  261.           WriteF('Examine requires Kickstart 2.04 V37+')
  262.           CleanUp(RETURN_FAIL)               -> Must exit before FreeArgs()!
  263.      ENDIF
  264.  
  265. ->>> Free resources
  266.  
  267.      IF fileidbase
  268.           IF fidb
  269.                id:=fidb.id                   -> Remember the ID number for later
  270.                FiFreeFileInfo(fidb)
  271.           ENDIF
  272.           CloseLibrary(fileidbase)
  273.      ENDIF
  274.      IF rdargs THEN FreeArgs(rdargs)
  275.      IF handle THEN Close(handle)
  276.      IF lock THEN UnLock(lock)
  277.  
  278. ->>> Set return code if requested
  279.  
  280.      IF arguments[ARETURNID]
  281.           IF id THEN rc:=id
  282.      ELSEIF arguments[ADIR]
  283.           IF fib THEN IF fib.direntrytype=2 THEN rc:=RETURN_WARN
  284.      ELSEIF arguments[AEXE]
  285.           longptr:=buffer
  286.           IF longptr[]=$3F3 THEN rc:=RETURN_WARN
  287.      ENDIF
  288.  
  289. ->>> Print error messages
  290.  
  291.      IF exception
  292.           SELECT exception
  293.                CASE "MEM"
  294.                     PrintFault(ERROR_NO_FREE_STORE, diskprogname)
  295.                CASE "fmat"
  296.                     PrintF('\s: unrecognised token (%\c)\n', diskprogname, exceptioninfo)
  297.                CASE EX_NO_FILEID
  298.                     PrintF('\s: could not open \s version \d+\n', diskprogname, {fileidname}, FILEIDVERS)
  299.                CASE EX_DOS_ERROR
  300.                     PrintFault(IoErr(), diskprogname)
  301.                CASE EX_ABOUT
  302.                     PrintF('\sUsage: \s \s\n', {version}+7, diskprogname, {template})  -> Ace!
  303.                CASE EX_DFORMAT_WITHOUT_FORMAT
  304.                     PrintF('\s: DFORMAT must be used with FORMAT\n', diskprogname)
  305.                DEFAULT
  306.                     PrintF('\s: error\n', diskprogname)
  307.           ENDSELECT
  308.           CleanUp(RETURN_FAIL)
  309.      ENDIF
  310.  
  311. ->>> Quit with correct return code
  312.  
  313.      CleanUp(rc)
  314. ENDPROC
  315. ->–
  316.  
  317. ->•Format Output({format string}, {output string})
  318. /*
  319. **   Takes a format string, parses it and writes it to the output string.
  320. */
  321. PROC formatoutput(format:PTR TO CHAR, output:PTR TO CHAR)
  322.      DEF char
  323.  
  324.      WHILE char:=format[]++
  325.           SELECT char
  326.                CASE "\\"                          -> That's just _one_ backslash!
  327.                     IF format[]++="n"
  328.                          output[]++:=10           -> Stick in an LF
  329.                     ELSE
  330.                          output[]++:="\\"         -> Not '\n', so print the "\" literally
  331.                     ENDIF
  332.                CASE "%"
  333.                     output:=parsetoken(format[]++, output)
  334.                CASE "¶"                           -> Might be useful - % clashes with C:List
  335.                     output:=parsetoken(format[]++, output)
  336.                DEFAULT
  337.                     output[]++:=char
  338.           ENDSELECT
  339.      ENDWHILE
  340. ENDPROC
  341. ->–
  342. ->•Parse Token(token, {output string}) = {new output position} <fmat(char)>
  343. /*
  344. **   Given a token and the output string, writes the correct information string
  345. **   to the output and updates the pointer.
  346. */
  347. PROC parsetoken(token, output:PTR TO CHAR)
  348.      DEF tempstr[FILENAME_LEN]:STRING, convstr[FILENAME_LEN]:STRING,
  349.          char,
  350.          bit, bits:PTR TO CHAR,
  351.          types:PTR TO CHAR,
  352.          byte
  353.  
  354.      SELECT token
  355.           CASE "a"
  356. ->•Protection bits (hsparwed) WORKS!
  357.                bits:='----apshdewr----'
  358.                FOR bit:=7 TO 0 STEP -1
  359.                     StrAdd(tempstr, IF fib.protection AND Shl(%1, bit) THEN
  360.                                        bits+bit ELSE bits+bit+8, 1)
  361.                ENDFOR
  362.                AstrCopy(output, tempstr, WHOCARES)
  363. ->–
  364.           CASE "b"
  365. ->•Size in blocks WORKS!
  366.                IF isdirectory
  367.                     AstrCopy(output, {notappl}, WHOCARES)
  368.                ELSE
  369.                     AstrCopy(output, StringF(convstr, '\d', fib.numblocks), WHOCARES)
  370.                ENDIF
  371. ->–
  372.           CASE "c"
  373. ->•Comment WORKS!
  374.                AstrCopy(output, IF fib.comment[] THEN fib.comment ELSE 'none', WHOCARES)
  375. ->–
  376.           CASE "d"
  377. ->•Date WORKS!
  378.                getdate()
  379.                AstrCopy(output, date, WHOCARES)
  380. ->–
  381.           CASE "e"
  382. ->•Entry type WORKS!
  383.                AstrCopy(output, IF fib.direntrytype=-3 THEN 'file' ELSE 'directory', WHOCARES)
  384. ->–
  385.           CASE "f"
  386. ->•Full path WORKS!
  387.                IF NameFromLock(lock, tempstr, FILENAME_LEN)=0 THEN Raise(EX_DOS_ERROR)
  388.                AstrCopy(output, tempstr, FilePart(tempstr)-tempstr+1)
  389. ->–
  390.           CASE "g"
  391. ->•FileID global file class bits (sfgmiepx) WORKS!
  392.                IF isdirectory
  393.                     AstrCopy(output, {notappl}, WHOCARES)
  394.                ELSE
  395.                     dofileid()
  396.                     bits:='xpeimgfs'
  397.                     FOR bit:=7 TO 0 STEP -1
  398.                          StrAdd(tempstr,
  399.                               IF fidb.globalfileclass AND Shl(%1, bit) THEN bits+bit ELSE '-', 1)
  400.                     ENDFOR
  401.                     AstrCopy(output, tempstr, WHOCARES)
  402.                ENDIF
  403. ->–
  404.           CASE "h"
  405. ->•Hex dump WORKS!
  406.                IF isdirectory
  407.                     AstrCopy(output, {notappl}, WHOCARES)
  408.                ELSE
  409.                     FOR byte:=0 TO Min(numbytes, length)-1
  410.                          IF ((byte AND 1)=FALSE) AND (byte<>0) THEN StrAdd(tempstr, ' ')
  411.                          StrAdd(tempstr, StringF(convstr, '\z\h[2]', buffer[byte]))
  412.                     ENDFOR
  413.                     AstrCopy(output, tempstr, WHOCARES)
  414.                ENDIF
  415. ->–
  416.           CASE "i"
  417. ->•FileID ID code WORKS!
  418.                IF isdirectory
  419.                     AstrCopy(output, {notappl}, WHOCARES)
  420.                ELSE
  421.                     dofileid()
  422.                     AstrCopy(output, StringF(convstr, '\d', fidb.id), WHOCARES)
  423.                ENDIF
  424. ->–
  425.           CASE "k"
  426. ->•Disk key WORKS!
  427.                AstrCopy(output, StringF(convstr, '\d', fib.diskkey), WHOCARES)
  428. ->–
  429.           CASE "l"
  430. ->•Size in bytes WORKS!
  431.                IF isdirectory
  432.                     AstrCopy(output, {notappl}, WHOCARES)
  433.                ELSE
  434.                     AstrCopy(output, StringF(convstr, '\d', fib.size), WHOCARES)
  435.                ENDIF
  436. ->–
  437.           CASE "n"
  438. ->•Filename WORKS!
  439.                AstrCopy(output, fib.filename, WHOCARES)
  440. ->–
  441.           CASE "o"
  442. ->•ASCII dump WORKS!
  443.                IF isdirectory
  444.                     AstrCopy(output, {notappl}, WHOCARES)
  445.                ELSE
  446.                     FOR byte:=0 TO Min(numbytes, length)-1
  447. ->                       IF ((byte AND 1)=FALSE) AND (byte<>0) THEN StrAdd(tempstr, ' ')
  448.                          StrAdd(tempstr, StringF(convstr, '\c',
  449.                               IF (buffer[byte]<" ") OR
  450.                                    ((buffer[byte]>=$80) AND (buffer[byte]<$A0))
  451.                                    THEN "." ELSE buffer[byte]))
  452.                          -> I'll have precedence with my logic operators pahleeese!
  453.                     ENDFOR
  454.                     AstrCopy(output, tempstr, WHOCARES)
  455.                ENDIF
  456. ->–
  457.           CASE "p"
  458. ->•Supplied path WORKS!
  459.                AstrCopy(output, path, WHOCARES)
  460. ->–
  461.           CASE "r"
  462. ->•FileID global file class text WORKS!
  463.                IF isdirectory
  464.                     AstrCopy(output, {notappl}, WHOCARES)
  465.                ELSE
  466.                     dofileid()
  467.                     types:=['(Executable)', '(Packed)', '(Encrypted)', '(IFF)',
  468.                             '(Music)', '(Graphics)', '(Text)', '(Script)']
  469.                     IF fidb.globalfileclass=0
  470.                          StrCopy(tempstr, '(None)')
  471.                     ELSE
  472.                          FOR bit:=0 TO 7
  473.                               IF fidb.globalfileclass AND Shl(%1, bit) THEN
  474.                                    StrAdd(tempstr, ListItem(types, bit))
  475.                          ENDFOR
  476.                     ENDIF
  477.                     AstrCopy(output, tempstr, WHOCARES)
  478.                ENDIF
  479. ->–
  480.           CASE "s"
  481. ->•FileID type name WORKS!
  482.                AstrCopy(output, describe(), WHOCARES)
  483. ->–
  484.           CASE "t"
  485. ->•Time WORKS!
  486.                getdate()
  487.                AstrCopy(output, time, WHOCARES)
  488. ->–
  489.           CASE "v"
  490. ->•Version (not implemented yet)
  491.                IF isdirectory
  492.                     AstrCopy(output, {notappl}, WHOCARES)
  493.                ELSE
  494.                     AstrCopy(output, 'version not implemented', WHOCARES)
  495.                ENDIF
  496. ->–
  497.           CASE "w"
  498. ->•Weekday WORKS!
  499.                getdate()
  500.                AstrCopy(output, day, WHOCARES)
  501. ->–
  502.           CASE "z"
  503. ->•'a' or 'an', depending on the first letter of %s WORKS!
  504.                dofileid()
  505.                char:=Char(describe()) AND %1011111     -> Uppercase the initial letter
  506.                AstrCopy(output, IF (char="A") OR (char="E") OR (char="I") OR
  507.                                    (char="O") OR (char="U") THEN 'an' ELSE 'a', WHOCARES)
  508.                                    -> Oooh!  Fussy!  ;-)
  509. ->–
  510.           CASE "%"
  511. ->•Percent sign WORKS!
  512.                AstrCopy(output, '%', WHOCARES)         -> What a waste!
  513. ->–
  514.           CASE "¶"
  515. ->•Paragraph mark WORKS!
  516.                AstrCopy(output, '¶', WHOCARES)
  517. ->–
  518.           DEFAULT
  519.                Throw("fmat", token)                    -> Unused token
  520.      ENDSELECT
  521.  
  522.      output:=output+StrLen(output)      -> Update the pointer
  523. ENDPROC output
  524. ->–
  525. ->•GetDate()
  526. PROC getdate()
  527.      DEF datetime:datetime
  528.  
  529.      IF day=NIL
  530.           datetime.stamp.days:=fib.datestamp.days
  531.           datetime.stamp.minute:=fib.datestamp.minute
  532.           datetime.stamp.tick:=fib.datestamp.tick
  533.           datetime.format:=FORMAT_DOS
  534.           datetime.flags:=DTF_SUBST
  535.           datetime.strday:=day:=String(DATE_LEN)
  536.           datetime.strdate:=date:=String(DATE_LEN)
  537.           datetime.strtime:=time:=String(DATE_LEN)
  538.           DateToStr(datetime)
  539.      ENDIF
  540. ENDPROC
  541. ->–
  542. ->•Do FileID()
  543. /*
  544. **   Just opens FileID.library and uses it to scan the file.  If that's already been
  545. **   done, it does nothing.
  546. */
  547. PROC dofileid()
  548.      IF fidb OR arguments[ANOFILEID] THEN RETURN
  549.  
  550.      IF fib.direntrytype<0         -> Don't bother if it's a directory
  551.           IF (fileidbase:=OpenLibrary({fileidname}, FILEIDVERS))=NIL THEN Raise(EX_NO_FILEID)
  552.           IF (fidb:=FiAllocFileInfo())=NIL THEN Raise("MEM")          -> Allocate info data structure
  553.           FiIdentify(buffer, fidb)                                    -> Examine buffer
  554.      ENDIF
  555. ENDPROC
  556. ->–
  557. ->•Describe() = description
  558. /*
  559. **   NOTE: if the %s token turns up more than once (it might!), the file may be
  560. **   examinebinary()ed each time.
  561. */
  562. PROC describe()
  563.      DEF description:PTR TO CHAR
  564.  
  565.      dofileid()
  566.      description:=String(DESCRIPTION_LEN)         -> Freed on CleanUp()
  567.           IF (fidb.id=0) OR (fidb=NIL)
  568.                examinebinary(description)
  569.           ELSE
  570.                description:=fidb.description
  571.           ENDIF
  572. ENDPROC description
  573. ->–
  574. ->• Examine Binary({description string})
  575. PROC examinebinary(description)
  576.      DEF a, type
  577.  
  578.      IF isdirectory
  579.           StrCopy(description, 'directory')
  580.           RETURN
  581.      ENDIF
  582.  
  583.      IF length>0
  584.           type:=PURE_ASCII
  585.           FOR a:=0 TO length-1
  586.                IF buffer[a]=0                                    -> Kludge!  $0s in file = binary
  587.                     type:=Max(type, BINARY)
  588.                ELSEIF (buffer[a]>127) AND (buffer[a]<160)        -> Amiga control character range
  589.                     type:=Max(type, OTHER_ASCII)
  590.                ELSEIF (buffer[a]>=160)                           -> Amiga extended character range
  591.                     type:=Max(type, AMIGA_ASCII)
  592.                ENDIF
  593.           ENDFOR
  594.      ELSE
  595.           type:=EMPTY_FILE
  596.      ENDIF
  597.  
  598.      StrCopy(description, ListItem(['pure ASCII text file',
  599.                                     'Amiga ASCII text file',
  600.                                     'unknown ASCII text file',
  601.                                     'unknown data file',         -> perhaps should be 'binary file'?
  602.                                     'empty file'], type))
  603. ENDPROC
  604. ->–
  605.  
  606. ->>> Static data
  607. ->• Version
  608. -> Standard version string for c:version.  "Version Examine FULL" will return the author string.
  609. version:
  610. CHAR 0, '$VER: Examine 1.4 (14/5/96) written by Neil Carter.\nPUBLIC DOMAIN ONLY.\n', 0
  611. ->–
  612. ->•Template
  613. template:
  614. CHAR 'FILENAME,BRIEF/S,FULL/S,Q=QUICK/S,NOFILEID/S,FORMAT/K,DFORMAT/K,NUMBYTES/K/N,LIST/S,ALL/S,VERSION/S,ID/S,RETURNID/S,EXE=EXECUTABLE/S,DIR=DIRECTORY/S,WINDOW/S', 0
  615. ->–
  616. ->•FileID name
  617. fileidname:
  618. CHAR 'FileID.library', 0
  619. ->–
  620. ->•Not Applicable
  621. notappl:
  622. CHAR 'N/A', 0
  623. ->–
  624.  
  625.